# 72. Django图书馆之书籍的增删改查 - 多表(外键)

# 创建书籍项目包(Book)

Django项目app --> 项目中又分了一级Python包,不同的功能放到不同的包里面

创建app

python manage.py startapp Book

告诉Django创建了一个app

在settings.py找那个的INSTALLED_APPS中添加新创建的app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'Press.apps.PressConfig',
    'Book.apps.BookConfig',
]

# 创建书籍需要的ORM配置

在models.py文件中

 from django.db import models

# Create your models here.
from Press.models import Press_sql  ## 导入出版社的sql文件,方便下面使用外键关联出版社的表


class Book_sql(models.Model):
    id = models.AutoField(primary_key=True)  ## 书籍ID
    name = models.CharField(max_length=12)  ## 书籍名称
    press = models.ForeignKey(to=Press_sql, on_delete=models.CASCADE)  ## 书籍跟出版社关联字段

写在models.py文件中,如果有关数据库操作的,比如,添加表、增加行、删除行、修改行等,都需要执行以下的步骤,让需要变更的操作,先记录在子项目的migrations目录中,在进行提交更改

生成记录命令:python3 manage.py makemigrations

提交变更记录修改命令:python3 manage.py migrate

# 业务逻辑代码

# 业务逻辑之查询

主要代码

## html代码
{% for i in book_user %} {# 循环过滤从后端传递过来的数据 #}
	<tr>
    	<td>{{ forloop.counter }}</td>
    	<td class="hidden"> {{ i.press.id }} </td>
    	{# 获取每行的ID,但让其不显示,为JS逻辑需要的代码 #}
    	<td class="hidden"> {{ i.id }} </td>
    	{# 获取每行的ID,但让其不显示,为后端逻辑需要的代码 #}
		<td>{{ i.name }}</td>
		<td>{{ i.press.name }}</td>
    	{# 获取到的press字段数据是一个对象,关联表中对应的对象,可以直接通过对象拿到对应的数据 #}
    	<td>
      		<a class="btn btn-info book_edit" role="button">编辑</a>
      		<a href="/book_del/?id={{ i.id }}" class="btn btn-danger" role="button">删除</a>
      		{# 通过a标签的特点,让点击后,带ID的值返回给后端 #}
    	</td>
	</tr>
{% endfor %}
## 让后端把查到所有的出版社,并把对象返回给前端,前端使用模板语言中的for循环,循环展示数据


## 后端函数代码
from django.shortcuts import render, redirect
from Book.models import Book_sql
from Press.models import Press_sql
                
# 查询

def book_subject(request):
    book_user = Book_sql.objects.all()  # 查询书籍表中的所有数据
    book_press = Press_sql.objects.all()  # 查询出版社表中的所有数据,创建书籍时,需要选择出版社
    return render(request, "book.html", {'book_user': book_user, 'book_press': book_press})  # 返回

# 业务逻辑之添加

主要代码

## 后端函数代码
from django.shortcuts import render, redirect
from Book.models import Book_sql
from Press.models import Press_sql

# 添加

def book_add(request):
    book_name = request.POST.get("book_name")  # 获取前端传递过来的POST类型,值为book_name的数据
    press_id = request.POST.get("press_id")  # 获取前端传递过来的POST类型,值为book_id的数据
    Book_sql.objects.create(name=book_name, press_id=press_id)  # 把前端传递过来的数据增加到数据库中
    return redirect('/book/')  # 返回


## 通过前端传来的数据,对数据进行插入

# 业务逻辑之删除

主要代码

## html代码
<a href="/book_del/?id={{ i.id }}" class="btn btn-danger" role="button">删除</a>
{# 通过a标签的特点,让点击后,带ID的值返回给后端 #}
## 通过点击链接的方式,返回要删除的ID


## 后端函数代码
from django.shortcuts import render, redirect
from Book.models import Book_sql
from Press.models import Press_sql

# 删除

def book_del(request):
    book_id = request.GET.get("id")  # 获取前端传递过来的GET类型,值为id的数据
    Book_sql.objects.filter(id=book_id).delete()  # 删除指定条件的所有数据
    return redirect('/book/')  # 返回

# 业务逻辑之编辑

主要代码

## html代码
<a class="btn btn-info book_edit" role="button">编辑</a>
## 对于怎么获取ID,请看下面的完整代码


## 后端函数代码
from django.shortcuts import render, redirect
from Book.models import Book_sql
from Press.models import Press_sql

def book_edit(request):
    book_id = request.POST.get("book_id")  # 获取前端传递过来的GET类型,值为book_id的数据
    book_name = request.POST.get("book_name")  # 获取前端传递过来的GET类型,值为book_name的数据
    press_id = request.POST.get("press_id")  # 获取前端传递过来的GET类型,值为press_id的数据
    boot_edit_sql = Book_sql.objects.get(id=book_id)  # 获取指定条件的数据库对象
    boot_edit_sql.name = book_name  # 修改内存中对象的值
    boot_edit_sql.press_id = press_id # 修改内存中对象的值
    boot_edit_sql.save()  # 将内存中的对象值重新提交给数据库,如果值发生变化,数据库自动同步修改
    return redirect('/book/')  # 返回

# 项目完整代码

QJnVwF.png

# models.py文件

from django.db import models

# Create your models here.
from Press.models import Press_sql  # 导入出版社的sql文件,方便下面使用外键关联出版社的表


class Book_sql(models.Model):
    id = models.AutoField(primary_key=True)  ## 书籍ID
    name = models.CharField(max_length=12)  ## 书籍名称
    press = models.ForeignKey(to=Press_sql, on_delete=models.CASCADE)  ## 书籍跟出版社关联字段

# urls.py文件

from Press import views as prees
from Book import views as book

urlpatterns = [
    # ---------> 出版社 <---------
    url(r'^press/', prees.press_subject),
    url(r'^press_add/', prees.press_add),
    url(r'^press_del/', prees.press_del),
    url(r'^press_edit/', prees.press_edit),

    # ---------> 书籍 <---------
    url(r'^book/', book.book_subject),
    url(r'^book_add/', book.book_add),
    url(r'^book_del/', book.book_del),
    url(r'^book_edit/', book.book_edit),

]

# views.py文件

from django.shortcuts import render, redirect
from Book.models import Book_sql
from Press.models import Press_sql


# Create your views here.


# 查询

def book_subject(request):
    book_user = Book_sql.objects.all()  # 查询书籍表中的所有数据
    book_press = Press_sql.objects.all()  # 查询出版社表中的所有数据,创建书籍时,需要选择出版社
    return render(request, "book.html", {'book_user': book_user, 'book_press': book_press})  # 返回


# 添加

def book_add(request):
    book_name = request.POST.get("book_name")  # 获取前端传递过来的POST类型,值为book_name的数据
    press_id = request.POST.get("press_id")  # 获取前端传递过来的POST类型,值为book_id的数据
    Book_sql.objects.create(name=book_name, press_id=press_id)  # 把前端传递过来的数据增加到数据库中
    return redirect('/book/')  # 返回


# 删除

def book_del(request):
    book_id = request.GET.get("id")  # 获取前端传递过来的GET类型,值为id的数据
    Book_sql.objects.filter(id=book_id).delete()  # 删除指定条件的所有数据
    return redirect('/book/')  # 返回


# 编辑

def book_edit(request):
    book_id = request.POST.get("book_id")  # 获取前端传递过来的GET类型,值为book_id的数据
    book_name = request.POST.get("book_name")  # 获取前端传递过来的GET类型,值为book_name的数据
    press_id = request.POST.get("press_id")  # 获取前端传递过来的GET类型,值为press_id的数据
    boot_edit_sql = Book_sql.objects.get(id=book_id)  # 获取指定条件的数据库对象
    boot_edit_sql.name = book_name  # 修改内存中对象的值
    boot_edit_sql.press_id = press_id  # 修改内存中对象的值
    boot_edit_sql.save()  # 将内存中的对象值重新提交给数据库,如果值发生变化,数据库自动同步修改
    return redirect('/book/')  # 返回

# book.html文件

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta http-equiv="content-Type" charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 描述网页,与之对应的属性值为content
    <meta name="keywords" content="">
    <meta name="description" content=""> -->
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <script src="/static/jquery-3.4.1.min.js"></script>
    <style>
        {# 显示的类 #}
        .show {
            display: block !important;
        }

        {# 不显示的类 #}
        .hidden {
            display: none !important;
        }
    </style>
    <title>书籍大全</title>
</head>
<body>
<div class="container-fluid">
    <div class="page-header">
        <h1>书籍大全</h1>
    </div>
    <div class="panel panel-default panel-primary">
        <div class="panel-heading">书籍大全操作界面</div>
        <div class="panel-body">
            <button type="button" class="btn btn-success Add_to pull-right" data-toggle="modal"
                    data-target=".bs-example-modal-sm"
                    data-target="#myModal" style="margin-right: 15px; margin-bottom: 15px">
                添加书籍
            </button>
            <table class="table table-striped table-hover table-bordered">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>书籍</th>
                    <th>出版社</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% for i in book_user %} {# 循环过滤从后端传递过来的数据 #}
                    <tr>
                        <td>{{ forloop.counter }}</td>
                        <td class="hidden"> {{ i.press.id }} </td>
                        {# 获取每行的ID,但让其不显示,为JS逻辑需要的代码 #}
                        <td class="hidden"> {{ i.id }} </td>
                        {# 获取每行的ID,但让其不显示,为后端逻辑需要的代码 #}
                        <td>{{ i.name }}</td>
                        <td>{{ i.press.name }}</td>
                        {# 获取到的press字段数据是一个对象,关联表中对应的对象,可以直接通过对象拿到对应的数据 #}
                        <td>
                            <a class="btn btn-info book_edit" role="button">编辑</a>
                            <a href="/book_del/?id={{ i.id }}" class="btn btn-danger" role="button">删除</a>
                            {# 通过a标签的特点,让点击后,带ID的值返回给后端 #}
                        </td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>

{# 添加书籍开始 #}
<div class="modal  Add_to_frame bs-example-modal-sm" tabindex="-1" role="dialog"
     aria-labelledby="myLargeModalLabel">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close Add_to_frame_Close1" data-dismiss="modal" aria-label="Close"><span
                        aria-hidden="true">×</span></button>
                <h4 class="modal-title">添加书籍</h4>
            </div>
            <div class="modal-body">
                <form class="form-horizontal" action="/book_add/" method="post">
                    <div class="form-group">
                        <label class="col-sm-2 control-label">书籍名称</label>
                        <div class="col-sm-10">
                            <input type="text" name="book_name" class="form-control">
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-sm-2 control-label">出版社名称</label>
                        <div class="col-sm-10">
                            <select class="form-control" name="press_id">
                                {% for book_press_i in book_press %}
                                    <option value="{{ book_press_i.id }}">{{ book_press_i.name }}</option>
                                {% endfor %}
                            </select>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default Add_to_frame_Close2" data-dismiss="modal">取消
                        </button>
                        <button type="submit" class="btn btn-primary">确定添加</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
{# 添加书籍结束 #}


{# 编辑书籍开始 #}
<div class="modal  edit_frame bs-example-modal-sm" tabindex="-1" role="dialog"
     aria-labelledby="myLargeModalLabel">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close edit_frame_Close1" data-dismiss="modal" aria-label="Close"><span
                        aria-hidden="true">×</span></button>
                <h4 class="modal-title">编辑书籍</h4>
            </div>
            <div class="modal-body">
                <form class="form-horizontal" action="/book_edit/" method="post">
                    <div class="form-group">
                        <label class="col-sm-2 control-label">书籍名称</label>
                        <div class="col-sm-10">
                            <input type="text" name="book_id" class="form-control edit_value_id hidden">
                            <input type="text" name="book_name" class="form-control edit_value_name">
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-sm-2 control-label">出版社名称</label>
                        <div class="col-sm-10">
                            <select class="form-control edit_value_press" name="press_id">
                                {% for book_press_i in book_press %}
                                    <option value="{{ book_press_i.id }}">{{ book_press_i.name }}</option>
                                {% endfor %}
                            </select>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default edit_frame_Close2" data-dismiss="modal">取消
                        </button>
                        <button type="submit" class="btn btn-primary">确定编辑</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
{# 编辑书籍结束 #}

</body>
<script>
    {#  添加框的点击开启事件  #}
    $(".Add_to").click(function () {
        $(".Add_to_frame").addClass("show");
    });

    {#  添加框的关闭事件  #}
    $(".Add_to_frame_Close1").click(function () {
        $(".Add_to_frame").removeClass("show");
    });
    $(".Add_to_frame_Close2").click(function () {
        $(".Add_to_frame").removeClass("show");
    });

    {#  编辑框的点击开启事件  #}
    $(".book_edit").click(function () {
        $(".edit_frame").addClass("show");
        var press_id = $(this).parent().prev().prev().prev().prev().text().trim(); {# 获取到当前点击行的 #}
        var id = $(this).parent().prev().prev().prev().text(); {# 获取到当前点击行的数据库ID #}
        var name = $(this).parent().prev().prev().text(); {# 获取到当前点击行的name值 #}
        $(".edit_value_id").val(id); {# 获取到的值,显示到所在的输入框中 但ID框不显示,为后端传递数据需要 #}
        $(".edit_value_name").val(name); {# 获取到的值,显示到所在的输入框中 #}
        $("option[value='" + press_id + "']").prop("selected",true); {# 认为select下拉列表的动态默认值 #}
    });

    {#  编辑框的关闭事件  #}
    $(".edit_frame_Close2").click(function () {
        $(".edit_frame").removeClass("show");
    })  
    $(".edit_frame_Close1").click(function () {
        $(".edit_frame").removeClass("show");
    })

</script>
</html>